import argparse
import os
import shutil
import sys
from shutil import copy2

from loguru import logger


class GoBuild:
	"""
	一个用于构建跨平台执行文件的类。

	初始化函数，设置构建的主文件、生成的执行文件名称以及目标平台。
	:param f: 需要构建的主文件(例如: main.go)
	:param n: 生成的执行文件主名称(例如: install)
	"""

	def __init__(self, f, n=None):
		self.darwin = darwin
		self.go = "go"
		self.name = n
		self.arch_list = []
		self.os_list = []
		self.amd64 = False
		self.mips64 = False
		self.arm64 = False
		self.arm32 = False
		self.linux = False
		self.windows = False
		self.file = f
		self.basename = ""
		self.archs = "X86_64"
		self.os_type = ""
		self.exe = ""
		self.tmp = ""
		self.logger = logger

	def init(self):
		"""
		初始化函数，根据不同的架构和操作系统生成相应的架构和操作系统列表，
		并设置可执行文件的基础名称。
		"""
		# 检查并添加架构到架构列表
		if self.arm64:
			self.arch_list.append("arm64")
		if self.mips64:
			self.arch_list.append("mips64le")
		if self.amd64:
			self.arch_list.append("amd64")
		if self.arm32:
			self.arch_list.append("arm")

		# 检查并添加操作系统到操作系统列表
		if self.linux:
			self.os_list.append("linux")
		if self.windows:
			self.os_list.append("windows")
		if self.darwin:
			self.os_list.append("darwin")

		# 设置可执行文件的基础名称
		if self.name is None:
			self.basename = str(os.path.basename(self.file)).replace(".go", "")
		else:
			self.basename = self.name

	def delete(self):
		"""
		开始删除生成的临时文件
		:return: None
		"""
		# 构造临时文件的完整路径
		tmp = os.path.join(os.getcwd(), self.tmp)
		try:
			# 尝试删除临时文件
			os.remove(path=self.tmp)
			# 删除成功后记录日志
			self.logger.debug(f"删除成功: {tmp}")
		except Exception as e:
			# 删除失败后记录错误日志
			self.logger.error(f"删除出错 - [{tmp} ] : {str(e)}")

	def copy(self):
		"""
		复制执行文件到目标目录，并根据当前环境调整文件路径。
		此方法首先构建目标文件路径，然后尝试从临时文件路径复制文件到目标路径，
		如果临时文件存在的话。如果复制成功，它将调用delete方法删除临时文件。
		如果临时文件不存在，它将记录一个警告消息。
		"""
		# 构建目标文件路径
		dst = os.path.join("client", self.exe)
		# 将目标路径与当前工作目录结合
		dst = os.path.join(os.getcwd(), dst)
		# 替换路径中的'amd64'为'X86_64'以适配不同架构
		dst = str(dst).replace("amd64", "X86_64")
		# 记录复制操作的开始
		self.logger.debug(f"开始复制: {dst}")
		# 检查临时文件是否存在
		if os.path.isfile(self.tmp):
			try:
				# 尝试复制文件到目标路径
				copy2(src=self.tmp, dst=dst)
				# 复制成功后，删除临时文件
				self.delete()
			except Exception as e:
				# 如果复制过程中发生异常，记录错误信息
				self.logger.error(f"复制失败: {str(e)}")
		else:
			# 如果临时文件不存在，记录警告信息
			self.logger.warning(f"文件不存在: {self.tmp}")

	def build(self):
		"""
		构建指定的Go文件，根据操作系统类型和架构进行编译，并处理构建结果。

		1. 根据架构类型转换变量`self.archs`，确保其符合预期的架构命名规范。
		2. 记录构建系统的操作系统类型和架构，用于调试和追踪。
		3. 根据操作系统和架构生成可执行文件名`self.exe`，并调整Windows系统下的文件扩展名。
		4. 构建Go文件，如果构建成功则调用`self.copy()`方法处理构建结果，否则记录错误并退出程序。
		"""
		# 根据架构类型转换变量self.archs，确保其符合预期的架构命名规范
		if self.archs == "amd64":
			self.archs = "X86_64"

		# 记录构建系统的操作系统类型和架构，用于调试和追踪
		self.logger.debug(f"构建系统: {self.os_type}", )
		self.logger.debug(f"构建架构: {self.archs}")

		# 根据操作系统和架构生成可执行文件名self.exe，并调整Windows系统下的文件扩展名
		self.exe = self.basename + "_" + self.os_type + "-" + self.archs
		self.tmp = str(os.path.basename(self.file)).replace(".go", "")
		if self.os_type == "windows":
			self.exe = self.exe + ".exe"
			self.tmp = self.tmp + ".exe"

		# 构建Go文件
		c = f"{self.go} build {self.file}"
		if os.system(c) == 0:
			# 如果构建成功则记录信息并调用self.copy()方法处理构建结果
			self.logger.info(f"构建成功,正在生成: {self.exe}")
			self.copy()
		else:
			# 如果构建失败则记录错误并退出程序
			self.logger.error(f"构建失败: {self.exe}")
			print(c)
			sys.exit(2)

	def ost(self, o):
		"""
		设置操作系统类型

		该方法主要用于设置GOOS环境变量，以模拟不同的操作系统环境这对于后续的编译过程特别重要，
		因为GOOS环境变量决定了编译输出的目标操作系统。

		参数:
		o (str): 要模拟的操作系统类型，例如"linux"、"windows"等。

		返回:
		无
		"""
		# 设置GOOS环境变量以模拟指定的操作系统
		os.environ['GOOS'] = o
		# 更新实例的os_type属性以存储当前设置的操作系统类型
		self.os_type = o

	def arch(self, arch):
		"""
		设置架构并触发构建过程

		该方法接收一个架构名称，根据特定规则转换后设置环境变量GOARCH，
		并将该架构名称保存以供后续使用。最后，调用build方法进行构建。

		参数:
		arch (str): 构架名称，可能需要转换以适配特定的命名约定。

		返回:
		无
		"""
		# 根据输入的架构名称进行条件判断，以确定是否需要转换架构名称
		if arch == "X86_64":
			arch = "amd64"

		# 设置环境变量GOARCH为转换后的架构名称，以便在后续的构建过程中使用
		os.environ['GOARCH'] = arch

		# 保存当前架构名称到实例变量，以便在类的其他方法中访问
		self.archs = arch

		# 调用实例的build方法，触发针对指定架构的构建过程
		self.build()

	def start(self, save):
		"""
		启动初始化和操作系统处理流程

		在这个方法中，首先进行初始化操作，然后根据`save`参数检查目录是否存在，
		如果不存在则创建目录．随后，遍历操作系统列表，对每个操作系统进行处理．
		对于Linux操作系统，进一步遍历其架构列表并进行处理；对于其他操作系统，
		则默认使用X86_64架构进行处理．

		参数:
		save (str): 保存路径字符串，用于检查是否存在以及创建目录
		"""
		# 初始化操作
		self.init()
		# 检查save是否为目录，如果不是则创建client目录
		if not os.path.isdir(save):
			os.mkdir("./client")
		# 遍历操作系统列表，对每个操作系统调用ost方法
		for i in self.os_list:
			self.ost(i)
			# 对Linux操作系统，记录架构列表并遍历每个架构
			if i == "linux":
				self.logger.debug(f"架构列表: {self.arch_list}")
				for a in self.arch_list:
					self.arch(arch=a)
			# 对其他操作系统，默认使用X86_64架构
			else:
				self.arch(arch="X86_64")


# 主程序入口
if __name__ == "__main__":
	# 获取当前目录
	cwd = os.getcwd()
	# 解析命令传参
	parser = argparse.ArgumentParser()
	parser.add_argument("-f", "--file", help="源代码文件名", type=str, default="main.go")
	parser.add_argument("-n", "--name", help="项目名称", type=str, default="network-troubleshooting")

	# 是否启用Linux平台, 默认启用
	parser.add_argument("-l", "--linux", help="是否启用Linux平台", action='store_true', default=True)
	parser.add_argument("--no-linux", help="禁用Linux平台", action='store_false', dest='linux')

	# 是否启用Darwin平台, 默认启用
	parser.add_argument("-d", "--darwin", help="是否启用Darwin平台", action='store_true', default=False)
	parser.add_argument("--no-darwin", help="禁用Darwin平台", action='store_false', dest='darwin')

	# 是否启用Windows平台, 默认启用
	parser.add_argument("-w", "--windows", help="是否启用Windows平台", action='store_true', default=False)
	parser.add_argument("--no-windows", help="禁用Windows平台", action='store_false', dest='windows')

	# 是否启用arm64平台, 默认启用
	parser.add_argument("-a", "--arm64", help="是否启用arm64平台", action='store_true', default=True)
	parser.add_argument("--no-arm64", help="禁用arm64平台", action='store_false', dest='arm64')

	# 是否启用arm32平台, 默认启用
	parser.add_argument("-r32", "--arm32", help="是否启用arm32平台", action='store_true', default=True)
	parser.add_argument("--no-arm32", help="禁用arm32平台", action='store_false', dest='arm32')

	# 是否启用mips64平台, 默认启用
	parser.add_argument("-m", "--mips64", help="是否启用mips64平台", action='store_true', default=True)
	parser.add_argument("--no-mips64", help="禁用mips64平台", action='store_false', dest='mips64')

	# 是否启用amd64/x86平台, 默认启用
	parser.add_argument("-x", "--x86", help="是否启用amd64/x86平台", action='store_true', default=True)
	parser.add_argument("--no-x86", help="禁用amd64/x86平台", action='store_false', dest='x86')
	# 设置保存目录，默认: ./client
	client_ = os.path.join(cwd, 'client')
	parser.add_argument("-o", "--output", help=f"保存目录->默认: {client_}", type=str, default=client_)
	# 显示版本号
	parser.add_argument("-v", "--version", action='version', version='%(prog)s 1.0')
	# 解析命令行参数
	args = parser.parse_args()
	# 提取命令行参数中的文件名
	file = args.file
	# 提取命令行参数中的项目名称
	name = args.name
	# 提取命令行参数中的MIPS64平台编译选项
	mips64 = args.mips64
	# 提取命令行参数中的Linux平台编译选项
	linux = args.linux
	# 提取命令行参数中的Darwin平台编译选项
	darwin = args.darwin
	# 提取命令行参数中的ARM64平台编译选项
	arm64 = args.arm64
	# 提取命令行参数中的X86平台编译选项
	x86 = args.x86
	# 提取命令行参数中的ARM32平台编译选项
	arm32 = args.arm32
	# 提取命令行参数中的Windows平台编译选项
	windows = args.windows
	# 打印所有命令行参数
	print(args)
	# 打印提取的文件名
	print(f"文件名: {file}")
	# 打印提取的项目名称

	# 打印启用的平台
	print(f"Linux: {args.linux}")
	print(f"Darwin: {args.darwin}")
	print(f"Windows: {args.windows}")
	print(f"ARM64: {args.arm64}")
	print(f"ARM32: {args.arm32}")
	print(f"MIPS64: {args.mips64}")
	print(f"X86: {args.x86}")

	# 创建GoBuild实例，传入文件名、项目名称及各平台编译选项
	up = GoBuild(f=file, n=name)
	# 设置ARM32架构的编译选项
	up.arm32 = arm32
	# 设置Windows系统的编译选项
	up.windows = windows
	# 设置ARM64架构的编译选项
	up.arm64 = arm64
	# 设置MIPS64架构的编译选项
	up.mips64 = mips64
	# 设置X86架构的编译选项
	up.x86 = x86
	up.amd64 = x86
	# 设置Linux系统的编译选项
	up.linux = linux
	# 设置Darwin系统的编译选项
	up.darwin = darwin
	# 启动编译过程
	up.start(save=args.output)
